﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using SmartQuant;

namespace QuantBox
{
    public static class StreamerManagerExtensions
    {
        private static readonly FieldInfo IdStreamsField = null!;
        private static readonly FieldInfo DictStreamsField = null!;

        static StreamerManagerExtensions()
        {
            foreach (var field in typeof(StreamerManager).GetFields(BindingFlags.Instance | BindingFlags.NonPublic)) {
                if (field.FieldType == typeof(IdArray<ObjectStreamer>)) {
                    IdStreamsField = field;
                }

                if (field.FieldType == typeof(Dictionary<Type, ObjectStreamer>)) {
                    DictStreamsField = field;
                }
            }
        }

        public static IdArray<ObjectStreamer>? GetStreamers(this StreamerManager manager)
        {
            if (IdStreamsField == null) {
                return null;
            }

            return (IdArray<ObjectStreamer>)IdStreamsField.GetValue(manager);
        }

        public static Dictionary<Type, ObjectStreamer>? GetStreamerDict(this StreamerManager manager)
        {
            if (IdStreamsField == null) {
                return null;
            }

            return (Dictionary<Type, ObjectStreamer>)DictStreamsField.GetValue(manager);
        }
    }

    public class ObjectTableStreamer : ObjectStreamer
    {
        private IdArray<ObjectStreamer>? _streamers;
        private Dictionary<Type, ObjectStreamer>? _dictStreamer;

        public ObjectTableStreamer()
        {
            typeId = 24;
            type = typeof(ObjectTable);

        }

        private object Deserialize(BinaryReader reader)
        {
            GetStreamers();
            int id = reader.ReadByte();
            version = reader.ReadByte();
            return _streamers![id].Read(reader, version);
        }

        private void Serialize(BinaryWriter writer, object obj)
        {
            GetStreamers();
            var key = obj.GetType();
            if (_dictStreamer!.TryGetValue(key, out var value))
            {
                writer.Write(value.typeId);
                writer.Write(value.GetVersion(obj));
                value.Write(writer, obj);
            }
            else
            {
                //Console.WriteLine("StreamerManager::ObjectStreamer not found, type:{0}", type);
            }
        }

        private void GetStreamers()
        {
            if (_streamers == null) {
                _streamers = streamerManager.GetStreamers();
                _dictStreamer = streamerManager.GetStreamerDict();
            }
        }

        public override object Read(BinaryReader reader, byte version)
        {
            var objectTable = new ObjectTable();
            int id;
            while ((id = reader.ReadInt32()) != -1) {
                objectTable[id] = Deserialize(reader);
            }
            return objectTable;
        }

        public override void Write(BinaryWriter writer, object obj)
        {
            var objectTable = (ObjectTable)obj;
            for (var i = 0; i < objectTable.Size; i++) {
                if (objectTable[i] != null) {
                    writer.Write(i);
                    var field = objectTable[i];
                    if (field is DataObject dataObject) {
                        streamerManager.Serialize(writer, dataObject);
                    }
                    else {
                        Serialize(writer, field);
                    }
                }
            }
            writer.Write(-1);
        }
    }
}